﻿/*
 * File      : xml_file.c
 * projects manager
 * Change Logs:
 * Date			Author			Notes
 * 2013.7.22	jimmy.lee		first version
 */
//#include <time.h>
#include <string.h>
#include <rtthread.h>
#include <dfs_posix.h>
#include "mxml.h"

/*
 * Globals...
 */

int event_counts[6];


/*
 * Local functions...
 */

void		sax_cb(mxml_node_t *node, mxml_sax_event_t event, void *data);
mxml_type_t	type_cb(mxml_node_t *node);
const char	*whitespace_cb(mxml_node_t *node, int where);

mxml_node_t *xml_new(void)
{
	mxml_node_t *tree;		// XML tree
	mxml_node_t *node;		// Node which should be in test.xml	
	int i;
	static const char	*types[] =	// Strings for node types
	{
		"MXML_ELEMENT",
		"MXML_INTEGER",
		"MXML_OPAQUE",
		"MXML_REAL",
		"MXML_TEXT"
	};	

/*
* Test the basic functionality...
*/

	// 建立顶级element
	tree = mxmlNewElement(MXML_NO_PARENT, "element");

	if (RT_NULL == tree)
	{
		rt_kprintf("ERROR: No parent node in basic test!\n");
		return RT_NULL;
	}

	if (tree->type != MXML_ELEMENT)
	{
		rt_kprintf("ERROR: Parent has type %s (%d), expected MXML_ELEMENT!\n",
					tree->type < MXML_ELEMENT || tree->type > MXML_TEXT ?
					"UNKNOWN" : types[tree->type], tree->type);
		mxmlDelete(tree);
		return RT_NULL;
	}

	if (strcmp(tree->value.element.name, "element"))
	{
		rt_kprintf("ERROR: Parent value is \"%s\", expected \"element\"!\n",
					tree->value.element.name);
		mxmlDelete(tree);
		return RT_NULL;
	}

	mxmlNewInteger(tree, 123);
	mxmlNewOpaque(tree, "opaque");
	mxmlNewReal(tree, 123.4f);
	mxmlNewText(tree, 1, "text");

	mxmlLoadString(tree, "<group type='string'>string string string</group>",
					MXML_NO_CALLBACK);
	mxmlLoadString(tree, "<group type='integer'>1 2 3</group>",
					MXML_INTEGER_CALLBACK);
	mxmlLoadString(tree, "<group type='real'>1.0 2.0 3.0</group>",
					MXML_REAL_CALLBACK);
	mxmlLoadString(tree, "<group>opaque opaque opaque</group>",
					MXML_OPAQUE_CALLBACK);
	mxmlLoadString(tree, "<foo><bar><one><two>value<two>value2</two></two></one></bar></foo>", 
					MXML_OPAQUE_CALLBACK);

	node = tree->child;

	if (RT_NULL == node)
	{
		rt_kprintf("ERROR: No first child node in basic test!\n");
		mxmlDelete(tree);
		return RT_NULL;
	}

	if (node->type != MXML_INTEGER)
	{
		rt_kprintf("ERROR: First child has type %s (%d), expected MXML_INTEGER!\n",
					node->type < MXML_ELEMENT || node->type > MXML_TEXT ?
					"UNKNOWN" : types[node->type], node->type);
		mxmlDelete(tree);
		return RT_NULL;
	}

	if (node->value.integer != 123)
	{
		rt_kprintf("ERROR: First child value is %d, expected 123!\n",
					node->value.integer);
		mxmlDelete(tree);
		return RT_NULL;
	}

	node = node->next;

	if (!node)
	{
		rt_kprintf("ERROR: No second child node in basic test!\n");
		mxmlDelete(tree);
		return RT_NULL;
	}

	if (node->type != MXML_OPAQUE)
	{
		rt_kprintf("ERROR: Second child has type %s (%d), expected MXML_OPAQUE!\n",
					node->type < MXML_ELEMENT || node->type > MXML_TEXT ?
					"UNKNOWN" : types[node->type], node->type);
		mxmlDelete(tree);
		return RT_NULL;
	}

	if (!node->value.opaque || strcmp(node->value.opaque, "opaque"))
	{
		rt_kprintf("ERROR: Second child value is \"%s\", expected \"opaque\"!\n",
					node->value.opaque ? node->value.opaque : "(null)");
		mxmlDelete(tree);
		return RT_NULL;
	}

	node = node->next;

	if (!node)
	{
		rt_kprintf("ERROR: No third child node in basic test!\n");
		mxmlDelete(tree);
		return RT_NULL;
	}

	if (node->type != MXML_REAL)
	{
		rt_kprintf("ERROR: Third child has type %s (%d), expected MXML_REAL!\n",
					node->type < MXML_ELEMENT || node->type > MXML_TEXT ?
					"UNKNOWN" : types[node->type], node->type);
		mxmlDelete(tree);
		return RT_NULL;
	}

	if (node->value.real != 123.4f)
	{
		rt_kprintf("ERROR: Third child value is %f, expected 123.4!\n",
					node->value.real);
		mxmlDelete(tree);
		return RT_NULL;
	}

	node = node->next;

	if (!node)
	{
		rt_kprintf("ERROR: No fourth child node in basic test!\n");
		mxmlDelete(tree);
		return RT_NULL;
	}

	if (node->type != MXML_TEXT)
	{
		rt_kprintf("ERROR: Fourth child has type %s (%d), expected MXML_TEXT!\n",
					node->type < MXML_ELEMENT || node->type > MXML_TEXT ?
					"UNKNOWN" : types[node->type], node->type);
		mxmlDelete(tree);
		return RT_NULL;
	}

	if (!node->value.text.whitespace ||
		!node->value.text.string || strcmp(node->value.text.string, "text"))
	{
		rt_kprintf("ERROR: Fourth child value is %d,\"%s\", expected 1,\"text\"!\n",
					node->value.text.whitespace,
					node->value.text.string ? node->value.text.string : "(null)");
		mxmlDelete(tree);
		return RT_NULL;
	}

	for (i = 0; i < 4; i ++)
	{
		node = node->next;

		if (!node)
		{
			rt_kprintf("ERROR: No group #%d child node in basic test!\n", i + 1);
			mxmlDelete(tree);
			return RT_NULL;
		}

		if (node->type != MXML_ELEMENT)
		{
			rt_kprintf("ERROR: Group child #%d has type %s (%d), expected MXML_ELEMENT!\n",
						i + 1, node->type < MXML_ELEMENT || node->type > MXML_TEXT ?
						"UNKNOWN" : types[node->type], node->type);
			mxmlDelete(tree);
			return RT_NULL;
		}
	}
	return tree;	
}



/*
 * 'sax_cb()' - Process nodes via SAX.
 */

void
sax_cb(mxml_node_t      *node,		// I - Current node
       mxml_sax_event_t event,		// I - SAX event
       void             *data)		// I - SAX user data
{
 /*
  * This SAX callback just counts the different events.
  */

  event_counts[event] ++;
}


/*
 * 'type_cb()' - XML data type callback for mxmlLoadFile()...
 */

mxml_type_t				// O - Data type
type_cb(mxml_node_t *node)		// I - Element node
{
  const char	*type;			// Type string


 /*
  * You can lookup attributes and/or use the element name, hierarchy, etc...
  */

  if ((type = mxmlElementGetAttr(node, "type")) == NULL)
    type = node->value.element.name;

  if (!strcmp(type, "integer"))
    return (MXML_INTEGER);
  else if (!strcmp(type, "opaque") || !strcmp(type, "pre"))
    return (MXML_OPAQUE);
  else if (!strcmp(type, "real"))
    return (MXML_REAL);
  else
    return (MXML_TEXT);
}


/*
 * 'whitespace_cb()' - Let the mxmlSaveFile() function know when to insert
 *                     newlines and tabs...
 */

const char *				// O - Whitespace string or NULL
	whitespace_cb(mxml_node_t *node,	// I - Element node
              int         where)	// I - Open or close tag?
{
  mxml_node_t	*parent;		// Parent node
  int		level;			// Indentation level
  const char	*name;			// Name of element
  static const char *tabs = "\t\t\t\t\t\t\t\t";
					// Tabs for indentation


 /*
  * We can conditionally break to a new line before or after any element.
  * These are just common HTML elements...
  */

  name = node->value.element.name;

  if (!strcmp(name, "html") || !strcmp(name, "head") || !strcmp(name, "body") ||
      !strcmp(name, "pre") || !strcmp(name, "p") ||
      !strcmp(name, "h1") || !strcmp(name, "h2") || !strcmp(name, "h3") ||
      !strcmp(name, "h4") || !strcmp(name, "h5") || !strcmp(name, "h6"))
  {
   /*
    * Newlines before open and after close...
    */

    if (where == MXML_WS_BEFORE_OPEN || where == MXML_WS_AFTER_CLOSE)
      return ("\n");
  }
  else if (!strcmp(name, "dl") || !strcmp(name, "ol") || !strcmp(name, "ul"))
  {
   /*
    * Put a newline before and after list elements...
    */

    return ("\n");
  }
  else if (!strcmp(name, "dd") || !strcmp(name, "dt") || !strcmp(name, "li"))
  {
   /*
    * Put a tab before <li>'s, <dd>'s, and <dt>'s, and a newline after them...
    */

    if (where == MXML_WS_BEFORE_OPEN)
      return ("\t");
    else if (where == MXML_WS_AFTER_CLOSE)
      return ("\n");
  }
  else if (!strncmp(name, "?xml", 4))
  {
    if (where == MXML_WS_AFTER_OPEN)
      return ("\n");
    else
      return (NULL);
  }
  else if (where == MXML_WS_BEFORE_OPEN ||
           ((!strcmp(name, "choice") || !strcmp(name, "option")) &&
	    where == MXML_WS_BEFORE_CLOSE))
  {
    for (level = -1, parent = node->parent;
         parent;
	 level ++, parent = parent->parent);

    if (level > 8)
      level = 8;
    else if (level < 0)
      level = 0;

    return (tabs + 8 - level);
  }
  else if (where == MXML_WS_AFTER_CLOSE ||
           ((!strcmp(name, "group") || !strcmp(name, "option") ||
	     !strcmp(name, "choice")) &&
            where == MXML_WS_AFTER_OPEN))
    return ("\n");
  else if (where == MXML_WS_AFTER_OPEN && !node->child)
    return ("\n");

 /*
  * Return NULL for no added whitespace...
  */

  return (NULL);
}


//创建 XML 文档:在内存中创建和更新XML文档，使用 mxmlNew 一系列函数. 
mxml_node_t *xml_new_measuring(void)
{
	
	mxml_node_t *xml;    /* <?xml ... ?> */
	mxml_node_t *tree;
	mxml_node_t *data;   /* <data> */
	mxml_node_t *node;   /* <node> */
	mxml_node_t *group;  /* <group> */

	
	// 创建所有XML文件都需要的标头<?xml version="1.0"?> :
	xml = mxmlNewXML("1.0");
	// 根下创建<body>节点: <body theme='MENU'>...</body>
	tree = mxmlNewElement(xml, "body");
	mxmlElementSetAttr(tree, "theme", "MENU");
	// <body>下创建节点:<title type="CONST">测试属性</title>
	node = mxmlNewElement(tree, "title");
	mxmlElementSetAttr(node, "type", "CONST");
	mxmlNewText(node, 1, "测试属性");	
	// <body>下创建节点:<items type="CONST">...</items>
	node = mxmlNewElement(tree, "items");
	mxmlElementSetAttr(node, "type", "CONST");	
	
	tree = node;
	
	// <items>下创建节点:<item>...</items>
	
	{	
		//time_t now;
		node = mxmlNewElement(tree, "item");
		mxmlElementSetAttrf(node, "id", "C%02d", 1);
		//now = time(RT_NULL);
		mxmlNewTextf(node, 1, "测试时间:%s", "2013-07-22 16:01");//ctime(&now));
	}		
	{	
		double e, n;
		e = 23.4323;
		n = 123.4455;
		node = mxmlNewElement(tree, "item");
		mxmlElementSetAttrf(node, "id", "C%02d", 2);
		mxmlNewTextf(node, 1, "操作坐标:%0fdN %08fE", e, n);
	}

	{	
		const char *measuring_method = "电阻率二维断面";
		node = mxmlNewElement(tree, "item");
		mxmlElementSetAttrf(node, "id", "C%02d", 3);
		mxmlNewTextf(node, 1, "测试方法:%s", measuring_method);
	}
	{	
		#define TAPS_PER_HEADER	12
		int l_header, r_header;
		l_header = 5;
		r_header = 5;		
		node = mxmlNewElement(tree, "item");
		mxmlElementSetAttrf(node, "id", "C%02d", 4);
		mxmlNewTextf(node, 1, "电缆中继:L%02d + R%02d", l_header, r_header);
		
		node = mxmlNewElement(tree, "item");
		mxmlElementSetAttrf(node, "id", "C%02d", 5);
		mxmlNewTextf(node, 1, "电缆抽头:L%d + R%d", l_header * TAPS_PER_HEADER, r_header * TAPS_PER_HEADER);		
	}
	{	
		const char *man = "李先知";
		node = mxmlNewElement(tree, "item");
		mxmlElementSetAttrf(node, "id", "C%02d", 6);
		mxmlNewTextf(node, 1, "操作员:%s", man);
	}	
	{	
		const char *man = "袁泉";
		node = mxmlNewElement(tree, "item");
		mxmlElementSetAttrf(node, "id", "C%02d", 7);
		mxmlNewTextf(node, 1, "质检员:%s", man);
	}	
	{	
		const char *data_name = "D1307121110001.dat";
		node = mxmlNewElement(tree, "item");
		node = mxmlNewElement(node, "path");
		mxmlNewTextf(node, 1, "%s", data_name);
	}		

	return xml;
}


mxml_node_t *xml_load(const char *filename)
{
	mxml_node_t *tree;
	int fd;
	if ((fd = open(filename, O_RDONLY, 0)) < 0)
	{
		rt_kprintf("ERROR: xml open \"%s\" failed!\n", filename);
		return RT_NULL;
	}
	
	tree = mxmlLoadFd(NULL, fd,  MXML_NO_CALLBACK);//MXML_TEXT_CALLBACK);
	if (RT_NULL == tree)
		rt_kprintf("ERROR: xml Load file \"%s\" failed!", filename);
	
	close(fd);
	return tree;
}

int xml_save(const char *filename, mxml_node_t *tree)
{
	int fd;
	RT_ASSERT(RT_NULL != tree);
	
	if ((fd = open(filename,  O_WRONLY | O_CREAT | O_TRUNC, 0)) < 0)
	{
		rt_kprintf("ERROR: xml open \"%s\" failed!\n", filename);
		return (-1);
	}
	
    mxmlSaveFd(tree, fd, MXML_NO_CALLBACK);
    close(fd);	
	return 0;
}
mxml_node_t *xml_find(const char *filename, const char *path)
{
	mxml_node_t *tree, *node;
	tree = xml_load(filename);
	if (NULL == tree)
		return NULL;
	node = mxmlFindPath(tree, path);
	if (NULL == node)
		return NULL;

	return node;
}
// Test mxmlFindPath...通过指定路径找出对应node.
// 路径:例如 "*/two", "foo/*/two", "foo/bar/one/two", "foo/one/two", "bar/two/one", "*/one"

mxml_node_t *xml_find_path(mxml_node_t *tree, const char *path)
{
	mxml_node_t *node;

	if (NULL == tree)
		return NULL;
	
	node = mxmlFindPath(tree, path);
	if (RT_NULL == node)
	{
		rt_kprintf("ERROR: Unable to find value for \"%s\".\n", path);
// 		mxmlDelete(tree);
		return RT_NULL;
	}
// 	else if (node->type != MXML_OPAQUE || strcmp(node->value.opaque, "value"))
// 	{
// 		rt_kprintf("ERROR: Bad value for \"%s\".\n", path);
//  		mxmlDelete(node);
// 		return RT_NULL;
// 	}
	return node;
}

int xml_test(void)
{
	mxml_node_t *xml, *node;
// 	char path[64];
	char *buffer;
	int size;
	const char *filepath = "measuring.xml";
	
 	xml = xml_new_measuring();
//	xml = xml_new();
	if (RT_NULL == xml)
	{
		rt_kprintf("ERROR: create a xml tree for test failed!\n");
		return (-1);
	}	
	
	rt_kprintf("Create a new xml tree and save as \"%s\":\n", filepath);
	cat(filepath);
	xml_save(filepath, xml);
	
	mxmlDelete(xml);
	
// 	rt_kprintf("Load xml tree from \"%s\" and save as \"%s\":\n", "project.xml", "new.xml");
	xml = xml_load(filepath);
	xml_save("new.xml", xml);
	
// 	node = xml_find_path(xml, "items/*/path");
	node = xml_find_path(xml, "*/items");

	buffer = rt_malloc(1024);
	if (mxmlSaveString(node, buffer, 1024, whitespace_cb) > 0)
		rt_kprintf(buffer);	
	rt_free(buffer);
	
	mxmlDelete(node);
	mxmlDelete(xml);
		
		
	return 0;
}

#ifdef RT_USING_FINSH
#include <finsh.h>
FINSH_FUNCTION_EXPORT(xml_load, mini-xml load test);
FINSH_FUNCTION_EXPORT(xml_find, mini-xml[xml file name, string path for node] test for find a node from path);
FINSH_FUNCTION_EXPORT(xml_save, mini-xml save test);
FINSH_FUNCTION_EXPORT(xml_test, create a new test xml file with filename);
#endif

/*
 * End of "$Id: testmxml.c 439 2011-04-13 15:43:32Z mike $".
 */
